/* Copyright (C) 2014-2016 Dan Chapman <dpniel@ubuntu.com>

   This file is part of Dekko mail client

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License or (at your option) version 3 or any later version
   accepted by the membership of KDE e.V. (or its successor approved
   by the membership of KDE e.V.), which shall act as a proxy
   defined in Section 14 of version 3 of the license.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
// NOTE: uncomment to enable profiling
//#define QML_COMPILER_STATS
#include <QApplication>
#include <QtQuick>
#include <QDebug>
#include <QQmlContext>
#include <QSettings>
#include <QtGui/QGuiApplication>
#include <QtQml/QtQml>
#include <QtQuick/QQuickView>
#include <QTranslator>
#include <QLocale>
#include <QScopedPointer>
#include "AppVersion/SetCoreApplication.h"
#include "Common/Application.h"
#include "Common/MetaTypes.h"
#include "Common/SettingsNames.h"
#include "static_plugins.h"
#include "DekkoGlobal.h"
#include "app/Settings/SettingsFileBase.h"
#include "app/Settings/MailboxSettings.h"
#include "app/Settings/NotificationSettings.h"
#include "app/Settings/ViewSettings.h"
#include "app/Utils/Path.h"
#include "app/Utils/CustomQQuickViewFactory.h"
#include "app/Network/MsgPartNetAccessManagerFactory.h"
#include "configure.cmake.h"

#ifdef UNCONFINED
#include <QCommandLineParser>
#endif

static bool checkForStaleLockFile(QLockFile **lockFile, QString &filePath, QString &errorMessage);

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    Q_INIT_RESOURCE(actionicons);
    Q_INIT_RESOURCE(providericons);

    //Install translations
    QTranslator dekkoTranslator;
    QTranslator dekkoBuildTranslator;
    // It would appear Qt has a real problem loading translations for anywhere but in the same directory
    // as the executable. I have no idea if this is click package related or a bug in Qt but this works
    // so i'm not going to complain.... (*much*)
    QString trPath;
#ifdef CLICK_MODE
    trPath = app.applicationDirPath();
#else
    trPath = QStringLiteral(DESKTOP_TR_PATH);
#endif
    qDebug() << "LOCATION: " << QLibraryInfo::location(QLibraryInfo::TranslationsPath);
    if (dekkoTranslator.load(QLocale::system().name(), trPath)) {
        qDebug() << "Translation loaded: " << !dekkoTranslator.isEmpty();
        app.installTranslator(&dekkoTranslator);
    } else if (dekkoBuildTranslator.load(QLocale::system().name(), QCoreApplication::applicationDirPath() + QStringLiteral("/locale/"))) {
        qDebug() << "Installing translations in build dir";
        app.installTranslator(&dekkoBuildTranslator);
    } else {
        qWarning() << "Failed loading translations";
    }

#ifdef UNCONFINED
    QCommandLineParser parser;
    parser.setApplicationDescription(QStringLiteral("Dekko email client"));
    parser.addVersionOption();
    parser.addHelpOption();
    parser.addPositionalArgument(QStringLiteral("mailto"), QStringLiteral("Start a compose session using the mailto: url scheme"));
    parser.process(app);
#endif

    Common::registerMetaTypes();
#ifdef CLICK_MODE
    Common::Application::name = QStringLiteral(APP_ID);
    // If the organization is set, QStandardPaths::DataLocation will be
    // $PREFIX/$APPNAME/$ORGANIZATION e.g.:
    // /usr/share/dekko.dekkoproject/dekko.dekkoproject
    Common::Application::organization = QStringLiteral(APP_ID);
#else
    Common::Application::name = QStringLiteral(APP_NAME);
    // If the organization is set, QStandardPaths::DataLocation will be
    // $PREFIX/$APPNAME/$ORGANIZATION e.g.:
    // /usr/share/dekko.dekkoproject/dekko.dekkoproject
    Common::Application::organization = QStringLiteral(APP_ORGANIZATION);
#endif

    AppVersion::setVersion();
    AppVersion::setCoreApplicationData();

    QString error;
    // Configure mbox settings
    QScopedPointer<Dekko::Settings::SettingsFileBase> mboxSettings(new Dekko::Settings::SettingsFileBase);
    Dekko::Settings::MailboxSettings::setDefaultFile(mboxSettings.data());
    QLockFile *mboxLock = 0;
    QString mlPath = Dekko::configLocation(QStringLiteral("mailboxConfig.json.lock"));
    if (!checkForStaleLockFile(&mboxLock, mlPath , error)) {
        qCritical() << error;
        return 1;
    }
    mboxSettings.data()->setPath(Dekko::configLocation(QStringLiteral("mailboxConfig.json")));
    QScopedPointer<QLockFile> mboxLockFile(mboxLock);
    // Notification settings
    QScopedPointer<Dekko::Settings::SettingsFileBase> notificationSettings(new Dekko::Settings::SettingsFileBase);
    Dekko::Settings::NotificationSettings::setSettingsFile(notificationSettings.data());
    QLockFile *notifyLock = 0;
    QString nlPath = Dekko::configLocation(QStringLiteral("notifications.json.lock"));
    if (!checkForStaleLockFile(&notifyLock, nlPath, error)) {
        qCritical() << error;
        return 1;
    }
    notificationSettings.data()->setPath(Dekko::configLocation(QStringLiteral("notifications.json")));
    QScopedPointer<QLockFile> notifyLockFile(notifyLock);
    // View settings
    QScopedPointer<Dekko::Settings::SettingsFileBase> viewSettings(new Dekko::Settings::SettingsFileBase);
    Dekko::Settings::ViewSettings::setViewSettingsFile(viewSettings.data());
    QLockFile *viewLock = 0;
    QString vlPath = Dekko::configLocation(QStringLiteral("viewSettings.json.lock"));
    if (!checkForStaleLockFile(&viewLock, vlPath, error)) {
        qCritical() << error;
        return 1;
    }
    viewSettings.data()->setPath(Dekko::configLocation(QStringLiteral("viewSettings.json")));
    QScopedPointer<QLockFile> viewLockFile(viewLock);

    QSharedPointer<QQuickView> qQuickViewer = Dekko::Utils::CustomQQuickViewFactory::instance();
    qQuickViewer->setResizeMode(QQuickView::SizeRootObjectToView);
    app.addLibraryPath(QCoreApplication::applicationDirPath());


    // let's first check all standard locations
    QString qmlFile = Dekko::findQmlFile(QStringLiteral("qml/main.qml"));
    QUrl addressbookStoreLocation(QStringLiteral(
                                      "file://%1/.local/share/dekko.dekkoproject/addressbook.vcard")
                                  .arg(QDir::homePath()));
    qQuickViewer->engine()->rootContext()->setContextProperty(QStringLiteral("addressbookStoreLocation"), QVariant::fromValue(addressbookStoreLocation));

    Dekko::Network::MsgPartNetAccessManagerFactory msgPartNetAccessManagerFactory;
    qQuickViewer->engine()->setNetworkAccessManagerFactory(&msgPartNetAccessManagerFactory);
    qQuickViewer->engine()->rootContext()->setContextProperty(QStringLiteral("msgPartNetAccessManagerFactory"), &msgPartNetAccessManagerFactory);
    qQuickViewer->engine()->rootContext()->setContextProperty(QStringLiteral("appUris"), QString::fromLatin1(qgetenv("APP_URIS")));

#ifdef WITH_CONVERGENCE
    qQuickViewer->engine()->rootContext()->setContextProperty(QStringLiteral("convergenceEnabled"), QVariant(true));
#else
    qQuickViewer->engine()->rootContext()->setContextProperty(QLatin1String("convergenceEnabled"), QVariant(false));
#endif
    QStringList mailToArgs;
#ifdef UNCONFINED
    mailToArgs << parser.positionalArguments();
#endif
    qQuickViewer->engine()->rootContext()->setContextProperty(QStringLiteral("mailToArgs"), mailToArgs);
    // Context property to figure out if we are on unity8/mir or not
    qQuickViewer->engine()->rootContext()->setContextProperty(QStringLiteral("isRunningOnMir"), QVariant(qgetenv("QT_QPA_PLATFORM") == "ubuntumirclient"));

    DekkoGlobal::registerDekkoTypes("DekkoCore");
    DekkoGlobal::registerTrojitaCoreTypes("TrojitaCore");

    qQuickViewer->setTitle(QObject::trUtf8("Dekko"));
    qQuickViewer->setSource(QUrl::fromLocalFile(qmlFile));
    qQuickViewer->show();
    return app.exec();
}

static bool checkForStaleLockFile(QLockFile **lockFile, QString &filePath, QString &errorMessage)
{
    QDir dir(Dekko::configLocation(QString()));
    if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) {
        errorMessage = QStringLiteral("Cannot create directory: %1").arg(dir.path());
        return false;
    }

    // Reset to config directory for consistency; avoid depending on this behavior for paths
    if (QDir::setCurrent(dir.absolutePath()) && dir.isRelative()) {
        dir.setPath(QStringLiteral("."));
    }
    QLockFile *lock = new QLockFile(filePath);
    *lockFile = lock;
    lock->setStaleLockTime(0);
    if (!lock->tryLock()) {
        if (lock->error() == QLockFile::LockFailedError) {
            // This happens if a stale lock file exists and another process uses that PID.
            // Try removing the stale file, which will fail if a real process is holding a
            // file-level lock. A false error is more problematic than not locking properly
            // on corner-case systems.
            if (!lock->removeStaleLockFile() || !lock->tryLock()) {
                errorMessage = QStringLiteral("Configuration file is already in use");
                return false;
            } else
                qDebug() << "Removed stale lock file";
        } else {
            errorMessage = QStringLiteral("Cannot write configuration file (failed to acquire lock)");
            return false;
        }
    }
    return true;
}
